/**
 * 
 */
package com.google.code.cisx.web;

import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.DontValidate;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.validation.SimpleError;
import net.sourceforge.stripes.validation.ValidationError;

import com.google.code.cisx.model.CommonUpdatableModelEntity;
import com.google.code.cisx.servlets.RemoteServiceBasedActionBeanSupport;
import com.google.code.cisx.store.StoreService;
import com.google.code.cisx.store.api.EntityNotFoundException;
import com.google.code.cisx.store.api.StoreException;

/**
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @version $Id: ManageEntityActionBean.java 259 2008-11-29 13:18:52Z rahul.thakur.xdev $
 * @since 1.0
 */
public abstract class ManageEntityActionBean<T extends CommonUpdatableModelEntity> extends
    RemoteServiceBasedActionBeanSupport {

  private T entity;

  private Long id;

  /**
   * @return the entity
   */
  public T getEntity() {
    return entity;
  }

  /**
   * @param entity
   *          the entity to set
   */
  public void setEntity(T entity) {
    this.entity = entity;
  }

  /**
   * @return the id
   */
  public Long getId() {
    return id;
  }

  /**
   * @param id
   *          the id to set
   */
  public void setId(Long id) {
    this.id = id;
  }

  @DefaultHandler
  @DontValidate
  @HandlesEvent("View")
  public Resolution view() {
    Class<T> klass = getEntityClass();
    if (null == id) {
      // NO Id in the request implies this a new Entity
      try {
        this.entity = klass.newInstance();
      } catch (InstantiationException e) {
        ValidationError error = new SimpleError(String.format(
            "Error instantiating a new entity of type %s .[Error: %s]", klass.getSimpleName(), e));
        getContext().getValidationErrors().add("error", error);
      } catch (IllegalAccessException e) {
        ValidationError error = new SimpleError(String.format(
            "Error instantiating a new entity of type %s .[Error: %s]", klass.getSimpleName(), e));
        getContext().getValidationErrors().add("error", error);
      }
    } else {
      try {
        this.entity = query(klass, id);
      } catch (EntityNotFoundException e) {
        ValidationError error = new SimpleError(String.format("No entity of type %s could be located for Id: %s.",
            klass.getSimpleName(), id));
        getContext().getValidationErrors().add("error", error);
      } catch (StoreException e) {
        ValidationError error = new SimpleError(String.format(
            "Error obtaining entity of type %s could be located for Id: %s.", klass.getSimpleName(), id));
        getContext().getValidationErrors().add("error", error);
      }
    }

    Resolution resolution = getResolution(context);
    if (null == resolution)
      return new ForwardResolution("/error.jsp");
    else
      return resolution;
  }

  @DontValidate
  @HandlesEvent("Edit")
  public Resolution edit() {
    Class<T> klass = getEntityClass();
    if (null == id) {
      // ValidationError error = new
      // SimpleError(String.format("No %s ID selected for display.",
      // klass.getSimpleName()));
      // getContext().getValidationErrors().add("error", error);
      // NO Id in the request implies this a new Entity
      try {
        this.entity = klass.newInstance();
      } catch (InstantiationException e) {
        ValidationError error = new SimpleError(String.format(
            "Error instantiating a new entity of type %s .[Error: %s]", klass.getSimpleName(), e));
        getContext().getValidationErrors().add("error", error);
      } catch (IllegalAccessException e) {
        ValidationError error = new SimpleError(String.format(
            "Error instantiating a new entity of type %s .[Error: %s]", klass.getSimpleName(), e));
        getContext().getValidationErrors().add("error", error);
      }
    } else {
      try {
        this.entity = query(klass, id);
      } catch (EntityNotFoundException e) {
        ValidationError error = new SimpleError(String.format("No entity of type %s could be located for Id: %s.",
            klass.getSimpleName(), id));
        getContext().getValidationErrors().add("error", error);
      } catch (StoreException e) {
        ValidationError error = new SimpleError(String.format(
            "Error obtaining entity of type %s could be located for Id: %s.", klass.getSimpleName(), id));
        getContext().getValidationErrors().add("error", error);
      }
    }

    Resolution resolution = getResolution(context);
    if (null == resolution)
      return new ForwardResolution("/error.jsp");
    else
      return resolution;
  }

  @HandlesEvent("Save")
  public Resolution save() {
    Class<T> klass = getEntityClass();
    try {
      this.entity = StoreService.save(klass, entity);
    } catch (StoreException e) {
      throw new RuntimeException(e);
    }

    Resolution resolution = getResolution(context);
    if (null == resolution)
      return new ForwardResolution("/error.jsp");
    else
      return resolution;
  }

  /**
   * 
   * @param klass
   * @param entityId
   * @return
   * @throws StoreException
   * @throws EntityNotFoundException
   */
  private T query(Class<T> klass, Long entityId) throws EntityNotFoundException, StoreException {
    T obj = StoreService.lookup(klass, entityId);
    return obj;
  }

  /**
   * 
   * @return
   */
  protected abstract Class<T> getEntityClass();

  /**
   * Extensions are allowed to handle and return the appropriate resolutions based on passed in context.
   * 
   * @param context
   * @return
   */
  protected abstract Resolution getResolution(ActionBeanContext context);

  /**
   * Validates the entity's properties.
   */
  protected abstract void validate();
}
